import { switchCarToDriveMode } from '../../../api/api-engine-service';
import { createCar, getCar, getCars } from '../../../api/api-garage-service';
import { Component } from '../../../components/base-component';
import { ICar } from '../../../interfaces/car';
import { NAME_FIRST_PART, NAME_SECOND_PART } from '../../../utilities/car-names-list';
import {
  buttonNextState,
  buttonPrevState,
  carCount,
  maxPage,
  pageCounter,
  removingModal,
  updatingCars,
} from '../../../utilities/observable';
import { CarTrack } from '../cars/car-track';
import { Announce } from '../modal/modal';

const CARS_PER_PAGE = 7;

function getRandomNumber(arrayLength: number) {
  const maxFloored = Math.floor(arrayLength);
  return Math.floor(Math.random() * maxFloored);
}

export class CarListComponent extends Component {
  private pageCounter = 1;

  prevComponent: Component | null = null;

  private currentCars: CarTrack[] = [];

  private carsData: ({ id: number; time: number } | undefined)[] = [];

  constructor() {
    super({ tag: 'div', className: 'garage__cars' });

    this.createCarsPerPage();

    pageCounter.subscribe((page) => {
      this.pageCounter = page;
    });

    updatingCars.subscribe(() => {
      this.createCarsPerPage();
    });
  }

  async createOneCar(carConfigs: { name: string; color: string }) {
    const car: ICar = (await createCar(carConfigs.name, carConfigs.color)).content;
    carCount.notify((count) => {
      return count + 1;
    });
    const start = (this.pageCounter - 1) * CARS_PER_PAGE;
    const end = this.pageCounter * CARS_PER_PAGE;
    const carOptions = { id: car.id, name: car.name, color: car.color };
    if (car.id >= start && car.id <= end) {
      const component = new CarTrack({ className: `car-track__${car.id}`, carOptions });
      this.currentCars.push(component);
      this.prevComponent?.append(component);
      buttonNextState.notify(() => {
        return false;
      });
    }
  }

  async createCarsPerPage() {
    const cars = (await getCars()).content;
    maxPage.notify(() => {
      return cars.length;
    });
    const carsList = await getCars(this.pageCounter, CARS_PER_PAGE);
    if (this.pageCounter === 1) {
      buttonPrevState.notify(() => {
        return true;
      });
    } else {
      buttonPrevState.notify(() => {
        return false;
      });
    }

    if (this.pageCounter === Math.ceil(cars.length / 7)) {
      buttonNextState.notify(() => {
        return true;
      });
    } else {
      buttonNextState.notify(() => {
        return false;
      });
    }
    carCount.notify(() => {
      return cars.length;
    });
    const carContainer = new Component({ className: 'garage__cars-container' });
    this.prevComponent?.removeNode();
    this.prevComponent = carContainer;
    const arrayCars: CarTrack[] = [];
    carsList.content.forEach((carOptions: ICar) => {
      const component = new CarTrack({ className: `car-track__${carOptions.id}`, carOptions });
      arrayCars.push(component);
      carContainer.append(component);
    });
    this.currentCars = arrayCars;
    this.append(carContainer);
  }

  public generateCars() {
    const limit = 100;
    const maxVal = 16777215;
    for (let i = 0; i < limit; i += 1) {
      const indexFirstPart = getRandomNumber(NAME_FIRST_PART.length);
      const indexSecondPart = getRandomNumber(NAME_SECOND_PART.length);
      const name = `${NAME_FIRST_PART[indexFirstPart]} ${NAME_SECOND_PART[indexSecondPart]}`;
      const color = `#${Math.floor(Math.random() * maxVal).toString(16)}`;
      this.createOneCar({ name, color });
    }
  }

  async raceAllCars() {
    // eslint-disable-next-line consistent-return
    const racePromises = this.currentCars.map(async (el) => {
      const car = el.getCar();
      const carOptions = el.getCarOptions();
      try {
        const duration = await el.handleStatusCarEngine(carOptions, 'started');
        car.animateCar(duration);
        await switchCarToDriveMode(carOptions.id, 'drive');
        return {
          id: carOptions.id,
          time: duration,
        };
      } catch {
        car.stopAnimation();
      }
    });

    this.carsData = await Promise.all(racePromises);
    await this.createWinner();
  }

  toInitialState() {
    this.currentCars.forEach((el) => {
      const car = el.getCar();
      car.cancelAnimation();
    });
    removingModal.notify(() => {
      return true;
    });
  }

  async createWinner() {
    const resultsArray = this.carsData.filter(
      (el): el is { id: number; time: number } => el !== undefined
    );
    const winner = resultsArray.reduce(
      (min, current) => (current.time < min.time ? current : min),
      resultsArray[0]
    );
    const time = Number((winner.time / 1000).toFixed(3));

    let car: ICar;

    if (resultsArray.length !== 0) {
      try {
        car = (await getCar(winner.id)).content;
        this.append(new Announce(car.name, time));
      } catch {
        this.append(new Announce());
      }
    }
  }
}
